#pragma once
#include<iostream>
#include<fstream>
#include<thread>
#include<mutex>
#include<string>
#include<vector>
#include<unordered_map>
#include"util.hpp"
#include"log.hpp"

namespace ns_index
{
    struct DocInfo
    {
        std::string title;//文档的标题
        std::string content;//文档对应的去标签之后的内容
        std::string url;//官网文档URL
        uint64_t doc_id;//文档的id
    };

    struct InvertedElem
    {
        uint64_t doc_id;
        std::string word;
        int weight;
    };


    typedef std::vector<InvertedElem> InvertedList;
    class Index
    {
      private:
          Index()
          {}
          
          Index(const Index& )=delete;
          Index& operator=(const Index&)=delete;

          static Index* instance;
          static std::mutex mtx;
      public:
          ~Index()
          {}
      public:
          static Index*  GetInstance()
          {   
              if(nullptr==instance)
              {
                  mtx.lock();
                  if(nullptr==instance)
                  {
                      instance=new Index();
                  }
                  mtx.unlock();
              }
              return instance;
          }

          //根据doc_id 找到文档对应的内容
          DocInfo* GetForwardIndex(uint64_t doc_id)
          {
              if(doc_id>=forward_index.size())
              {
                std::cerr<<"doc_id out of arrange,error!"<<std::endl;
                return nullptr;
              }
              return &forward_index[doc_id];
          }
          
          //根据关键字word，获得倒排拉链
          InvertedList* GetInvertedList(const std::string& word)
          {
              auto iter=inverted_index.find(word);
              if(iter==inverted_index.end())
              {
                  std::cerr<<word<<" have no InvertedList"<<std::endl;
                  return nullptr;
              }
              return &(iter->second);
          }
          
          //根据去标签后，格式化后的文档，构建正排和倒排索引
          ///raw_html/raw.txt
          bool BuildIndex(const std::string& input)
          {
              std::ifstream in(input,std::ios::in|std::ios::binary);
              if(!in.is_open())
              {
                  std::cerr<<input<<" open fail"<<std::endl;
                  return false;
              }
              
              std::string line;
              int count=0;
              while(getline(in,line))
              {
                 DocInfo* doc=BuildForwardIndex(line);
                 if(nullptr==doc)
                 {
                    std::cerr<<"build "<<line<<" error"<<std::endl;
                    continue;
                 }

                BuildInvertedIndex(*doc);
                count++;
                if(count%100==0)
                {
                    //std::cout<<"当前已经建立的索引文档："<<count<<std::endl;
                    LOG(NORMAL,"前已经建立的索引文档："+std::to_string(count));
                }
              }
              in.close();
              return true;
          }
      
      private:
          DocInfo* BuildForwardIndex(const std::string& line)
          {
              //1.解析line，字符串切分
              std::vector<std::string>  results;
              const std::string sep("\3");
              ns_util::StringUtil::Split(line,&results,sep);
              if(results.size()!=3)
                  return nullptr;

              DocInfo doc;
              doc.title=results[0];
              doc.content=results[1];
              doc.url=results[2];
              doc.doc_id=forward_index.size();
              //插入到正排的索引中
              forward_index.push_back(std::move(doc));
              return &forward_index.back();
          }
          
          struct word_cnt
          {
              uint64_t title_cnt;
              uint64_t content_cnt;

              word_cnt():title_cnt(0),content_cnt(0)
            {}
          };

          bool BuildInvertedIndex(const DocInfo& doc)
          {
              //DocInfo[title,content,url,doc_id]
              //word ->倒排拉链
            std::unordered_map<std::string,word_cnt> word_map;//用来暂存词频的映射表
            
            //对标题进行分词，词频统计
            std::vector<std::string> title_words;
            ns_util::JiebaUtil::CutString(doc.title,&title_words);
            for(auto s:title_words)
            {
              boost::to_lower(s);//需要统一转换为小写
              word_map[s].title_cnt++;   
            }
            
            //对内容进行分词，词频统计
            std::vector<std::string> content_words;
            ns_util::JiebaUtil::CutString(doc.content,&content_words);
            for(auto s:content_words)
            {
              boost::to_lower(s);//需要统一转换为小写
              word_map[s].content_cnt++;
            }
#define X 10
#define Y 1
            for(auto& word_pair:word_map)
            {
                InvertedElem item;
                item.doc_id=doc.doc_id;
                item.word=word_pair.first;
                item.weight= X*word_pair.second.title_cnt+Y*word_pair.second.content_cnt;//相关性
                inverted_index[item.word].push_back(std::move(item));
            }
            
            return true;
          }

      private:
          //正排索引的数据结构用数组，下标就是天然的索引
          std::vector<DocInfo> forward_index;
          //倒排索引一定是一个关键字和一组InvertedElem对应，【关键字和倒排拉链的映射关系】
          std::unordered_map<std::string,InvertedList> inverted_index;
    };

    Index* Index::instance=nullptr;
    std::mutex Index::mtx; 
}
